package com.hcc.flow.server.service.sys;

import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.stereotype.Service;

import com.hcc.flow.server.common.enums.DataStatusType;
import com.hcc.flow.server.common.enums.IfStatusType;
import com.hcc.flow.server.common.utils.CommUtil;
import com.hcc.flow.server.common.utils.StringUtilsV2;
import com.hcc.flow.server.dao.sys.OrgDao;
import com.hcc.flow.server.dao.sys.PermissionDao;
import com.hcc.flow.server.dao.sys.RoleDao;
import com.hcc.flow.server.dao.sys.UserDao;
import com.hcc.flow.server.model.common.LoginUser;
import com.hcc.flow.server.model.common.Token;
import com.hcc.flow.server.model.sys.SysUser;
import com.hcc.flow.server.vo.sys.OrgOneVO;

import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

/**
 * token存到redis的实现类<br>
 * jwt实现的token
 * 
 * @author 韩长长 
 *
 */
@Primary
@Service
public class TokenService {

	private static final Logger log = LoggerFactory.getLogger("adminLogger");

	@Autowired
	private SysLogService logService;
	@Autowired
	private UserDao userDao;
	@Autowired
	private OrgDao orgDao;
	@Autowired
	private PermissionDao permissionDao;
	@Autowired
	private RoleDao roleDao;
	/**
	 * 私钥
	 */
	@Value("${token.jwtSecret}")
	private String jwtSecret;

	private static Key KEY = null;
	private static final String LOGIN_USER_KEY = "LOGIN_USER_KEY";

	
	public Token saveToken(LoginUser loginUser) {
		loginUser.setToken(CommUtil.uuid());
		cacheLoginUser(loginUser);
		// 登陆日志
		logService.save(loginUser.getUserId(), "登陆", true, null);
		String jwtToken = createJWTToken(loginUser);
		SysUser sysUser = userDao.getUserById(loginUser.getUserId());
		sysUser.setUserId(loginUser.getUserId());
		sysUser.setLastLoginTime(sysUser.getCurrentLoginTime());
		sysUser.setCurrentLoginTime(new Date(loginUser.getLoginTime()));
		userDao.update(sysUser);
		return new Token(jwtToken, loginUser.getLoginTime());
	}

	/**
	 * 生成jwt
	 * 
	 * @param loginUser
	 * @return
	 */
	private String createJWTToken(LoginUser loginUser) {
		Map<String, Object> claims = new HashMap<>();
		claims.put(LOGIN_USER_KEY, loginUser.getToken());// 放入一个随机字符串，通过该串可找到登陆用户

		String jwtToken = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS256, getKeyInstance()).compact();

		return jwtToken;
	}

	private void cacheLoginUser(LoginUser loginUser) {
		loginUser.setLoginTime(System.currentTimeMillis());
		loginUser.setExpireTime(loginUser.getLoginTime() + 7200 * 1000);
		// 根据uuid将loginUser缓存
		RedisServer.setCacheValue("login-user",getTokenKey(loginUser.getToken()), loginUser, 7200L);
	}

	/**
	 * 更新缓存的用户信息
	 */
	
	public void refresh(LoginUser loginUser,boolean clear,Integer sysId) {
		if(clear) {
			String token = loginUser.getToken();
			loginUser = loadUserByUserId(loginUser.getUserId(),sysId);
			loginUser.setToken(token);
		}
		cacheLoginUser(loginUser);
	}

	
	public LoginUser getLoginUser(String jwtToken) {
		String uuid = getUUIDFromJWT(jwtToken);
		if (uuid != null) {
			return (LoginUser) RedisServer.getCacheValue("login-user",getTokenKey(uuid));
		}
		return null;
	}

	
	public boolean deleteToken(String jwtToken) {
		String uuid = getUUIDFromJWT(jwtToken);
		if (uuid != null) {
			String key = getTokenKey(uuid);
			LoginUser loginUser = getLoginUser(key);
			if (loginUser != null) {
				RedisServer.remove("login-user",key);
				// 退出日志
				logService.save(loginUser.getUserId(), "退出", true, null);
				return true;
			}
		}
		return false;
	}

	private String getTokenKey(String uuid) {
		return "tokens:" + uuid;
	}

	private Key getKeyInstance() {
		if (KEY == null) {
			synchronized (TokenService.class) {
				if (KEY == null) {// 双重锁
					byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(jwtSecret);
					KEY = new SecretKeySpec(apiKeySecretBytes, SignatureAlgorithm.HS256.getJcaName());
				}
			}
		}

		return KEY;
	}

	private String getUUIDFromJWT(String jwtToken) {
		if ("null".equals(jwtToken) || StringUtilsV2.isBlank(jwtToken) || "undefined".equals(jwtToken)) {
			return null;
		}

		try {
			Map<String, Object> jwtClaims = Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(jwtToken)
					.getBody();
			return MapUtils.getString(jwtClaims, LOGIN_USER_KEY);
		} catch (ExpiredJwtException e) {
			log.error("{}已过期", jwtToken);
		} catch (Exception e) {
			log.error("{}", e);
		}

		return null;
	}
	
	private LoginUser loadUserByUserId(String userId,Integer sysId) {
		if (StringUtilsV2.isBlank(userId)) {
			throw new AuthenticationCredentialsNotFoundException("没有解析到用户账号");
		}
		SysUser sysUser = userDao.getUserById(userId);
		if (sysUser == null) {
			throw new AuthenticationCredentialsNotFoundException("用户名不存在");
		} else if (DataStatusType.STOP.getCode().equals(sysUser.getStatus())) {
			throw new LockedException("用户被锁定(停用),如有疑问，请联系管理员");
		} else if (DataStatusType.DELETE.getCode().equals(sysUser.getStatus())) {
			throw new DisabledException("用户已作废");
		} else if (IfStatusType.N.getCode().equals(sysUser.getIsLogin())) {
			throw new DisabledException("该用户不允许登录");
		}

		LoginUser loginUser = new LoginUser();
		BeanUtils.copyProperties(sysUser, loginUser);
		//用户所有角色ids
		String userRoleIds = roleDao.getRoleIdsByUserId(sysUser.getUserId());
		loginUser.setRoldIds(userRoleIds);
		Set<String> permissions;
		// 用户是超级管理员返回所有菜单数据
		if(loginUser.getIsSuperAdminRole())
			permissions = permissionDao.listPermissionCodeBySysId(sysId);
		else {
			permissions = permissionDao.listPermissionCodeByUserIdAndSysId(sysUser.getUserId(),sysId);
		}
		loginUser.setPermissions(permissions);
		if(StringUtilsV2.isNotBlank(loginUser.getOrgId())){
			OrgOneVO org = orgDao.getById(loginUser.getOrgId());
			if(org != null){
				loginUser.setOrgName(org.getOrgName());
				loginUser.setParentOrgId(org.getParentId());
				loginUser.setParentOrgName(org.getParentName());
			}
		}
		return loginUser;
	}
}
